package com.ctsi.framework.common.startrun;

import com.ctsi.commons.util.UtilFile;
import com.ctsi.commons.util.UtilValidate;
import com.ctsi.commons.util.factory.ObjectFact;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author: zhangjw
 * @Date: 2018/11/6 13:38
 * @Description: 方便获取ApplicationContext, 但是如果类重新装入ApplicationContext会变为null. 所以在热部署会有问题
 */
public abstract class ComponentFactory implements IStartRun, ObjectFact {
    private static final Logger log = LoggerFactory.getLogger(ComponentFactory.class);

    private static ServletContext servletContext = null;
    private static ApplicationContext applicationContext = null;
    private int order = -1;

    public final static ApplicationContext getApplicationContext() {
        if (applicationContext == null) {
            if (servletContext != null) {
                setApplicationContext(WebApplicationContextUtils.getWebApplicationContext(servletContext));
            } else {
                log.warn("servletContext is null");
            }
        }
        return applicationContext;
    }

    public static synchronized void setApplicationContext(ApplicationContext context) {
        log.debug("set applicationContext");
        applicationContext = context;
    }

    public final static Object getBean(final String id) {
        return getApplicationContext().getBean(id);
    }

    public static final <T> T getBean(final String id, Class<T> clazz) {
        return getApplicationContext().getBean(id, clazz);
    }

    public static ServletContext getServletContext() {
        return servletContext;
    }

    public static synchronized void setServletContext(ServletContext servletContext) {
        log.debug("set servletContext");
        ComponentFactory.servletContext = servletContext;
    }

    /**
     * 获取spring中指定类型的所有bean实例
     *
     * @param <T>
     * @param clazz
     * @return
     */
    public final static <T> List<T> getBeanForType(Class<T> clazz) {
        String[] beanNames = getApplicationContext().getBeanNamesForType(clazz);
        return getBeanForType(clazz, beanNames);
    }

    public final static <T> List<T> getBeanForType(Class<T> clazz, String[] beanNames) {
        if (UtilValidate.isEmpty(beanNames)) {
            log.info("no bean found for type {}", clazz);
            return new ArrayList<T>(0);
        }
        List<T> result = new ArrayList<T>(beanNames.length);
        for (String bn : beanNames) {
            log.debug("bean name is :{} type :{}", bn, clazz);
            result.add(getApplicationContext().getBean(bn, clazz));
        }
        return result;
    }

    /**
     * 重新初始化spring
     */
    public final static void reloadSpring() {
        ApplicationContext ac = getApplicationContext();
        if (ac instanceof AbstractApplicationContext) {
            log.info("refresh spring config");
            ((AbstractApplicationContext) ac).refresh();
        } else {
            log.warn("ApplicationContext not instance AbstractApplicationContext,can't refresh");
        }
    }

    /**
     * 返回web下实际路径,不能是文件 结尾一定带有路径分割符号 如果不存在，则不会增加分割符号
     *
     * @param path
     * @return
     */
    public final static String getRealPath(String path) {
        if (UtilValidate.isEmpty(path)) {
            path = "/";
        } else if (!path.startsWith("/")) {
            path = "/" + path;
        }
        String realPath = getServletContext().getRealPath(path);
        File f;
        if (UtilValidate.isEmpty(realPath)) {
            log.warn("ServletContext getRealPath for :{} return null ", path);
            File directory = new File(UtilFile.getUserDir(), path);
            try {
                realPath = directory.getCanonicalPath();
                log.info("  getRealPath for :{} is: {} ", path, realPath);
            } catch (IOException e) {
                log.error("getCanonicalPath error ", e);
                throw new RuntimeException(e);
            }
            f = directory;
        } else {
            f = new File(realPath);
        }

        if (f.isDirectory())
            return UtilFile.appendPathSeparator(realPath);
        else
            return realPath;
    }

    @Override
    public final void destroy() {
        synchronized (ComponentFactory.class) {
            applicationContext = null;
            servletContext = null;
        }
        log.debug("ComponentFactory is destroy");
    }

    @Override
    public final void initialize(ServletContext servletContext) {
        log.debug("ComponentFactory is run");
        setServletContext(servletContext);
        getApplicationContext();
    }

    @Override
    public boolean isRun() {
        return true;
    }

    @Override
    public final Object createObject(final String classKey) {
        return getApplicationContext().getBean(classKey);
    }

    @Override
    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }
}